package com.ruoyix.aicode.util;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import com.ruoyix.aicode.domain.Message;
import com.ruoyix.aicode.domain.PageSection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;

/**
 * LLM交互工具类
 */
public class LlmUtil {
    private static final Logger logger = LoggerFactory.getLogger(LlmUtil.class);
    private static final ObjectMapper objectMapper = new ObjectMapper();
    private static final WebClient webClient = WebClient.builder()
        .baseUrl("https://ark.cn-beijing.volces.com")
        .defaultHeader("Authorization", "Bearer f9c1148b-26fc-4c92-8977-fca871de1af3")
        .build();

    /**
     * 调用LLM API进行对话
     *
     * @param messages 对话消息列表
     * @param onChunkReceived 每个响应块处理函数
     * @return 完成信号
     */
    public static Mono<Void> callLLM(List<Map<String, String>> messages, Consumer<String> onChunkReceived) {
        logger.info("开始调用LLM API进行对话交互，消息数量: {}", messages.size());

        return webClient.post()
            .uri("/api/v1/chat/completions")
            .bodyValue(Map.of(
                "model", "deepseek-v3-250324",
                "messages", messages,
                "stream", true,
                "thinking", Map.of("type", "disabled")
            ))
            .retrieve()
            .bodyToFlux(String.class)
            .doOnNext(chunk -> {
                String processedChunk = processChunk(chunk);
                if (!processedChunk.isEmpty()) {
                    onChunkReceived.accept(processedChunk);
                }
            })
            .doOnComplete(() -> {
                logger.info("LLM响应流接收完毕");
            })
            .then();
    }

    /**
     * 处理LLM响应块
     *
     * @param chunk 原始响应块
     * @return 处理后的内容
     */
    public static String processChunk(String chunk) {
        try {
            // 删除可能的data:前缀，确保只处理JSON内容
            String processedChunk = chunk;
            if (processedChunk.startsWith("data: ")) {
                processedChunk = processedChunk.substring(6).trim();
            }

            // 跳过[DONE]消息
            if (processedChunk.equals("[DONE]")) {
                logger.debug("跳过[DONE]消息");
                return "";
            }

            try {
                JsonNode node = objectMapper.readTree(processedChunk);
                JsonNode choices = node.get("choices");
                if (choices != null && choices.isArray() && choices.size() > 0) {
                    JsonNode delta = choices.get(0).get("delta");
                    if (delta != null) {
                        JsonNode content = delta.get("content");
                        if (content != null && content.isTextual()) {
                            return content.asText();
                        }
                    }
                }
            } catch (Exception e) {
                logger.error("解析JSON失败: {}", e.getMessage(), e);
            }

            return "";
        } catch (Exception e) {
            logger.error("处理LLM响应出错: {}", e.getMessage(), e);
            return "";
        }
    }

    /**
     * 准备对话消息
     *
     * @param conversationHistory 对话历史
     * @param useGlobalContext 是否使用全局上下文
     * @param systemPrompt 系统提示
     * @param userPrompt 用户提示
     * @return 消息列表
     */
    public static List<Map<String, String>> prepareMessages(
            List<Message> conversationHistory,
            boolean useGlobalContext,
            String systemPrompt,
            String userPrompt) {

        // 添加转换上下文的系统消息
        Message systemMessage = new Message("system", systemPrompt);

        // 添加用户消息
        Message userMessage = new Message("user", userPrompt);

        // 准备发送的消息列表
        List<Map<String, String>> messages;

        // 根据全局上下文开关决定是否使用完整历史
        if (useGlobalContext) {
            // 使用完整的对话历史
            messages = new ArrayList<>();
            for (Message message : conversationHistory) {
                messages.add(message.toMap());
            }
            // 添加当前系统消息和用户消息
            messages.add(systemMessage.toMap());
            messages.add(userMessage.toMap());
            logger.info("使用全局上下文，累计对话历史消息数量: {}", messages.size());
        } else {
            // 只使用最近的系统消息和用户消息
            messages = new ArrayList<>();
            // 添加初始系统提示
            messages.add(new Message("system", "你是一个专业的前端开发助手，可以分析需求并生成前端代码。").toMap());
            // 添加当前阶段的系统提示
            messages.add(systemMessage.toMap());
            // 添加用户消息
            messages.add(userMessage.toMap());
            logger.info("不使用全局上下文，仅使用当前阶段消息，消息数量: {}", messages.size());
        }

        return messages;
    }

    /**
     * 生成Vue组件代码
     *
     * @param requirements 需求
     * @param section 页面部分
     * @param conversationHistory 对话历史
     * @param useGlobalContext 是否使用全局上下文
     * @return 组件内容
     */
    public static Mono<String> generateVueComponentForSection(
            String requirements,
            PageSection section,
            List<Message> conversationHistory,
            boolean useGlobalContext) {
        logger.info("为部分{}生成Vue组件", section.getId());

        // 构建Vue组件生成提示词
        String prompt = buildVueComponentPrompt(requirements, section);

        // 系统提示
        String systemPrompt = String.format("现在你正在生成'%s'组件，请作为一个专业的Vue组件开发者，创建美观、功能完善的Vue组件。请直接返回代码，不要包含任何标记或解释。", section.getId());

        // 准备消息
        List<Map<String, String>> messages = prepareMessages(
            conversationHistory, useGlobalContext, systemPrompt, prompt);

        final StringBuilder componentContent = new StringBuilder();

        return callLLM(messages, chunk -> {
            componentContent.append(chunk);
        })
        .thenReturn(componentContent.toString());
    }

    /**
     * 构建Vue组件提示词
     *
     * @param requirements 需求
     * @param section 页面部分
     * @return 组件提示词
     */
    private static String buildVueComponentPrompt(String requirements, PageSection section) {
        String prompt = String.format("""
            作为一个专业的Vue 3组件开发者，请根据以下需求生成一个完整的Vue单文件组件：

            需求描述: %s

            组件ID: %s

            请生成一个完整的Vue 3单文件组件，包含以下三个部分:
            1. <template> - 使用Vue 3语法，可以包含Element Plus组件
            2. <script> - 使用Composition API (setup)
            3. <style> - 可以使用scoped属性

            代码要求:
            1. 代码必须遵循Vue 3及ES6+的最佳实践
            2. 响应式设计，在不同设备上都能良好展示
            3. 注重用户体验，包含适当的交互反馈
            4. 使用Element Plus为UI组件库
            5. 注意组件之间的通信 (props, emits)
            6. 组件应该是独立的，便于复用
            7. 使用适当的命名和注释提高可维护性

            直接返回完整的Vue组件代码，不需要任何解释、说明或标记。
            """,
            requirements,
            section.getId());

        logger.debug("Vue组件提示词: {}", prompt);
        return prompt;
    }
}
